package com.gss.stockLedger.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import org.apache.log4j.Logger;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.gss.stockLedger.dao.QueryStockNoDaoSupportDAO;
import com.gss.stockLedger.domain.FIFOReportDTO;
import com.gss.stockLedger.domain.StockTransactionDtls;
import com.gss.stockLedger.form.ReportGenerateForm;

@Service
public class WACReportGenerationService {
	
	Logger logger = Logger.getLogger(getClass());
	/*@Autowired
	QueryStockDAO queryStockDAO;*/
	
	@Autowired
	QueryStockNoDaoSupportDAO queryStockNoDaoSupportDAO;
	
	/*@Autowired
	ExcelCreationService excelCreationService;*/
	
	double consumptionRate = 0.0F;
	
	private static Set<String> Consumption_Transaction_Type = new HashSet<String>();
	public int sheetCounter=0;
	static {
		Consumption_Transaction_Type.add("CONS");
		Consumption_Transaction_Type.add("FOC");
		Consumption_Transaction_Type.add("RTV");
		Consumption_Transaction_Type.add("SCRAP");
		Consumption_Transaction_Type.add("CAPTIVE");
		Consumption_Transaction_Type.add("SALES");
		
	}
	
	public boolean generateWACReport(ReportGenerateForm reportForm, Date date) {
			logger.debug("entered WAC");
			boolean resultOfSucess = false;
			GenerateExcelThread generateExcelThread = null;
			long startTime = System.currentTimeMillis();
		try{
		
			List<FIFOReportDTO> finalReportList;

			List<Object[]> partIdList = queryStockNoDaoSupportDAO.getUniquePartIds(reportForm.getInvType(),reportForm.getLastPartId(),reportForm.getCompanyName());
			
			LinkedList<FIFOReportDTO> transactionQ = null;
			Stack<FIFOReportDTO> consumedStack = null;
			LinkedList<FIFOReportDTO> transactionQForReport = null;
			System.out.println(partIdList.size());
			
			String sheetName = reportForm.getReportCategory() + reportForm.getReportType();				//added by ashish
			Workbook workbook = ExcelCreationService.createExcelFile(sheetName,reportForm.getValuationMethod(),reportForm.getStartDate(),
					reportForm.getEndDate(),date,reportForm.getReportCategory(),reportForm.getReportType(),reportForm.getInvType(), reportForm.getCompanyName());			//added by ashish
			logger.debug("default excel workbook created");			//added by ashish
			
			//create excel thread
			generateExcelThread = new GenerateExcelThread(workbook, reportForm, date);
			generateExcelThread.setRunning(true);
			
			generateExcelThread.start();
			
			
			for (Object[] object : partIdList) {
				
				finalReportList = new ArrayList<FIFOReportDTO>();
			
				List<StockTransactionDtls> stockTransactionDtls= queryStockNoDaoSupportDAO.getStockTransactionsForProduct(reportForm,object[0].toString(),object[1].toString());
				System.out.println("number of stock entries to process: " + stockTransactionDtls.size());
				if(stockTransactionDtls.size() == 0)
					continue;
				//sorted list according to receipt date
			
				transactionQ = new LinkedList<FIFOReportDTO>();
				transactionQForReport = new LinkedList<FIFOReportDTO>();
				consumedStack = new Stack<FIFOReportDTO>();
				
				long activeQuantity=0;
				float activeRate=0;
				double activeValue=0.0;
				float currentRate = 0.0F;
				FIFOReportDTO activeRow ;
			
				
			
				// in the following loop, all receipts are accumulated into a linked list so that they can be later used for consumption following FIFO
			
				for (StockTransactionDtls stockTransactionDtls2 : stockTransactionDtls) {
					activeRow = new FIFOReportDTO();
					BeanUtils.copyProperties(stockTransactionDtls2, activeRow);
					
					if(activeRow.getTransactionType().equalsIgnoreCase("ZGRIR")) {
						//change code to incorporate multiple grir entries on same day with same assignment number
						if(transactionQ.size()>0&&transactionQ.contains(activeRow))
						{
							int index =transactionQ.indexOf(activeRow);
							FIFOReportDTO fromQueue = transactionQ.get(index);
							fromQueue.setQuantity(fromQueue.getQuantity()+activeRow.getQuantity());
							fromQueue.setValue(fromQueue.getValue() + activeRow.getQuantity()*activeRow.getRate());
	// commented by Mayank
	//							if(fromQueue.getQuantity()<=0)
	//								transactionQ.remove(index);
						}
						else
						{
							FIFOReportDTO temp = new FIFOReportDTO();
							BeanUtils.copyProperties(activeRow, temp);
							temp.setValue((double) (temp.getQuantity() * temp.getRate()));
							transactionQ.add(temp);
						}
					}
				}
			
				// applying all adjustments on respective Purchases (GRIR)
			
				for (StockTransactionDtls stockTransactionDtls2 : stockTransactionDtls) {
					activeRow = new FIFOReportDTO();
					BeanUtils.copyProperties(stockTransactionDtls2, activeRow);
					
					if(activeRow.getTransactionType().equalsIgnoreCase("CUSTOM") || activeRow.getTransactionType().equalsIgnoreCase("COSTPRICE") || 
							activeRow.getTransactionType().equalsIgnoreCase("RETRO") || activeRow.getTransactionType().equalsIgnoreCase("FOREX")
							||activeRow.getTransactionType().equalsIgnoreCase("INVADJ")) {
						
						for (FIFOReportDTO fifoReportDTO : transactionQ) {
							if(null != activeRow.getAssignmentId() && activeRow.getAssignmentId().equalsIgnoreCase(fifoReportDTO.getAssignmentId())){
								if(activeRow.getTransactionType().equalsIgnoreCase("INVADJ")){
									if(activeRow.getQuantity()!=null)
									fifoReportDTO.setQuantity(fifoReportDTO.getQuantity()+activeRow.getQuantity());
								}
								if(activeRow.getValue()!=null)
									fifoReportDTO.setValue(fifoReportDTO.getValue() + activeRow.getValue());
								
								if(fifoReportDTO.getQuantity() != 0)
									fifoReportDTO.setRate((float) (fifoReportDTO.getValue() / fifoReportDTO.getQuantity()));
								else
									fifoReportDTO.setRate(0.0f);
								
								break;
							}
						}
					}
				}
			
				// above loop results into a transactionQ with all receipts along with their reversals and adjustments
				// running loop on this transactionQ, to remove any entries with zero or negative quantity or value
				List<FIFOReportDTO> toRemoveList = new ArrayList<FIFOReportDTO>();
				for(FIFOReportDTO receipts : transactionQ){
					if(receipts.getQuantity() <=0 || receipts.getValue() <=0){
						//transactionQ.remove(transactionQ.indexOf(receipts));
						toRemoveList.add(receipts);
					}else {
						receipts.setRate((float) (receipts.getValue() / receipts.getQuantity()));
					}
				}
				if(toRemoveList.size() > 0){
					for(FIFOReportDTO index : toRemoveList)
						transactionQ.remove(index);
				}
			
				// now we have transactionQ with all entries having positive Quantity and positive value and rate populated based on final values
			
				long counter=-1;
				FIFOReportDTO previousRow = null;
				for (StockTransactionDtls stockTransactionDtls2 : stockTransactionDtls) {
				
					activeRow = new FIFOReportDTO();
					BeanUtils.copyProperties(stockTransactionDtls2, activeRow);
					counter++;
					if(counter==0)
					{
						if(activeRow.getTransactionDate().after(reportForm.getStartDate())){
							finalReportList.add(activeRow);
						}
						
						if(activeRow.getTransactionType().equalsIgnoreCase("ZGRIR"))
						{
							FIFOReportDTO temp = new FIFOReportDTO();
							BeanUtils.copyProperties(activeRow, temp);
							if(transactionQ.size()>0 && transactionQ.contains(temp))
							{
								temp.setRate(transactionQ.getFirst().getRate());
								temp.setValue(transactionQ.getFirst().getValue());
								transactionQForReport.add(temp);
								transactionQ.removeFirst();
							}
							
							 consumptionRate = temp.getRate();
						}
						else if(activeRow.getTransactionType().equalsIgnoreCase("OPENING")){
							FIFOReportDTO temp = new FIFOReportDTO();
							BeanUtils.copyProperties(activeRow, temp);
							//temp.setRate((float) (temp.getValue() / temp.getQuantity()));
							temp.setValue((double) (temp.getRate()*temp.getQuantity()));
							temp.setAssignmentId("OpeningGRN");
							transactionQForReport.add(temp);
							
							//change to show value as product of quantity and rate
							activeRow.setValue((double) (temp.getRate()*temp.getQuantity()));
							
							consumptionRate = temp.getRate();
							//transactionQ.removeFirst();
						}else if(Consumption_Transaction_Type.contains(activeRow.getTransactionType())){
							
							FIFOReportDTO consumedStockDTO = new FIFOReportDTO();
							consumedStockDTO.setTransactionType("ZGRIR");
							consumedStockDTO.setAssignmentId("ApplicationGenerated");
							consumedStockDTO.setQuantity(activeRow.getQuantity());
							consumedStockDTO.setValue(0.0d);
							consumedStockDTO.setRate(currentRate);
							consumedStack.push(consumedStockDTO);
							
							activeRow.setQuantity(activeRow.getQuantity() * -1);
							activeRow.setRate(0.0f);
							activeRow.setValue(0.0d);
							consumptionRate = 0.0f;
						}
						
						activeRow.setClosingQuantity(activeRow.getQuantity()!=null?activeRow.getQuantity():0);
						activeRow.setClosingRate(activeRow.getRate()!=null?activeRow.getRate():0);
						activeRow.setClosingValue(activeRow.getValue()!=null?activeRow.getValue():0);
						activeQuantity = activeRow.getQuantity()!=null?activeRow.getQuantity():0;
						if(activeRow.getRate()!=null&&activeRow.getQuantity()!=null)
							activeValue = activeRow.getRate()*activeRow.getQuantity();
						else
							activeValue = activeRow.getValue();
	
						continue;
					}
					
					String transactionType = stockTransactionDtls2.getTransactionType();
					
					if(transactionType.equalsIgnoreCase("ZGRIR")) //grir is purchase 
					{
						FIFOReportDTO temp = new FIFOReportDTO();
						BeanUtils.copyProperties(activeRow, temp);
						if(transactionQ.size()>0 && transactionQ.contains(temp))
						{
							temp.setRate(transactionQ.getFirst().getRate());
							temp.setValue(transactionQ.getFirst().getValue());
							transactionQForReport.add(temp);
							transactionQ.removeFirst();
						}
						
						activeRow.setClosingQuantity(activeQuantity+=activeRow.getQuantity());
						activeRow.setClosingValue(activeRow.getValue()!=null?activeValue+=activeRow.getValue():activeValue);
						activeRow.setClosingRate((float) (activeValue/activeQuantity));
						if(activeRow.getClosingQuantity()!=0)
							consumptionRate = activeRow.getClosingValue()/activeRow.getClosingQuantity();
						else
							consumptionRate = 0;
					}
					else if(transactionType.equalsIgnoreCase("CPA"))
					{
						
					}
					else if(Consumption_Transaction_Type.contains(transactionType)){
						
						long consumedQuantity = 0;
						long quantityToConsume = activeRow.getQuantity();
						double consumedQuantityValue = consumptionRate*quantityToConsume;
						
	//					if(quantityToConsume>activeQuantity)
	//					{
	//						System.out.println("Invalid negative entry");
	//					}
	//					else
	//						System.out.println("valid entry");
						/*
						while(quantityToConsume>0)
						{
							
							if(transactionQForReport!=null&&transactionQForReport.size()>0)
							{
								FIFOReportDTO purchaseToConsume = transactionQForReport.getFirst();
							
								if(quantityToConsume>purchaseToConsume.getQuantity())
								{
									quantityToConsume-=purchaseToConsume.getQuantity();
									consumedQuantity+=purchaseToConsume.getQuantity();
									consumedQuantityValue+=(purchaseToConsume.getQuantity()*purchaseToConsume.getRate());
									transactionQForReport.removeFirst();
									currentRate = purchaseToConsume.getRate();
									
									if(consumedStack.peek().equals(purchaseToConsume)){
										consumedStack.peek().setQuantity((long) (purchaseToConsume.getValue() / purchaseToConsume.getRate()));
									}else{
										FIFOReportDTO temp = new FIFOReportDTO();
										BeanUtils.copyProperties(purchaseToConsume, temp);
										consumedStack.push(temp);
									}
									
									
								}
								else
								{
									purchaseToConsume.setQuantity(purchaseToConsume.getQuantity()-quantityToConsume);
									consumedQuantity += quantityToConsume;
									consumedQuantityValue +=quantityToConsume*purchaseToConsume.getRate();
									quantityToConsume =0;
									
									if(!consumedStack.empty()&&consumedStack.peek().equals(purchaseToConsume)){
										consumedStack.peek().setQuantity((long) (purchaseToConsume.getValue() / purchaseToConsume.getRate()) - purchaseToConsume.getQuantity());
									}else{
										FIFOReportDTO temp = new FIFOReportDTO();
										BeanUtils.copyProperties(purchaseToConsume, temp);
										temp.setQuantity((long) (purchaseToConsume.getValue() / purchaseToConsume.getRate()) - purchaseToConsume.getQuantity());
										consumedStack.push(temp);
									}
								}
							}
							else
							{
	//							System.out.println("inventory exhausted");
								FIFOReportDTO consumedStockDTO = new FIFOReportDTO();
								consumedStockDTO.setTransactionType("ZGRIR");
								consumedStockDTO.setAssignmentId("ApplicationGenerated");
								consumedStockDTO.setQuantity(quantityToConsume);
								if(currentRate != 0)
									consumedStockDTO.setValue((double) (quantityToConsume*currentRate));
								else
									consumedStockDTO.setValue(0.0d);
								consumedStockDTO.setRate(currentRate);
								consumedStack.push(consumedStockDTO);
								
								consumedQuantity+=quantityToConsume;
								consumedQuantityValue += quantityToConsume*currentRate;
								quantityToConsume=0;
								
							}
						}*/
	
						activeRow.setClosingQuantity(activeQuantity-=quantityToConsume); 
						activeRow.setClosingValue(activeValue-=consumedQuantityValue);
	
						if(activeQuantity!=0)
							activeRow.setClosingRate((float) (activeValue/activeQuantity));
						else
							activeRow.setClosingRate(0.0f);
						
						activeRow.setRate((float) consumptionRate);
						activeRow.setQuantity(quantityToConsume*-1);
						activeRow.setValue(consumedQuantityValue * -1);
						
						//wacReportList.add(activeRow);
					}
					/*else if(Consumption_Transaction_Type.contains(transactionType) && activeRow.getQuantity() < 0){/*
						long quantityToReverse = activeRow.getQuantity() * -1l;
						long reversedQuantity = 0;
						double reversedQuantityValue = 0.0d;
						while(quantityToReverse > 0){
							FIFOReportDTO consumptionEntry = consumedStack.peek();
							if(consumptionEntry.getQuantity()>quantityToReverse){
								consumptionEntry.setQuantity(consumptionEntry.getQuantity() - quantityToReverse);
								reversedQuantityValue+= quantityToReverse * consumptionEntry.getRate();
								if(!consumptionEntry.getAssignmentId().equalsIgnoreCase("ApplicationGenerated")){
									if(transactionQForReport.getFirst().equals(consumptionEntry)){
										transactionQForReport.getFirst().setQuantity(transactionQForReport.getFirst().getQuantity() + quantityToReverse);
									}else{
										FIFOReportDTO temp = new FIFOReportDTO();
										BeanUtils.copyProperties(consumptionEntry, temp);
										temp.setQuantity(quantityToReverse);
										transactionQForReport.add(temp);
									}
								}
								reversedQuantity+=quantityToReverse;
								quantityToReverse-=quantityToReverse;
							}else{
								
								if(!consumptionEntry.getAssignmentId().equalsIgnoreCase("ApplicationGenerated")){
									if(transactionQForReport.size()>0&&transactionQForReport.getFirst().equals(consumptionEntry)){
										transactionQForReport.getFirst().setQuantity(transactionQForReport.getFirst().getQuantity() + consumptionEntry.getQuantity());
									}else{
										FIFOReportDTO temp = new FIFOReportDTO();
										BeanUtils.copyProperties(consumptionEntry, temp);
										//temp.setQuantity(quantityToReverse);
										transactionQForReport.add(temp);
									}
								}
								reversedQuantityValue+=consumptionEntry.getQuantity()*consumptionEntry.getRate();
								reversedQuantity+=consumptionEntry.getQuantity();
								quantityToReverse-=consumptionEntry.getQuantity();
								consumedStack.pop();
							}
						}
					activeRow.setClosingQuantity(activeQuantity+=reversedQuantity); 
					activeRow.setClosingValue(activeValue+=reversedQuantityValue);
					activeRow.setClosingRate((float) (activeValue/activeQuantity));
					
					activeRow.setRate((float) (reversedQuantityValue/reversedQuantity));
					activeRow.setQuantity(activeRow.getQuantity() * -1);
					activeRow.setValue(reversedQuantityValue);
					
					}*/
					else if(activeRow.getTransactionType().equalsIgnoreCase("CUSTOM") || activeRow.getTransactionType().equalsIgnoreCase("COSTPRICE") || 
							activeRow.getTransactionType().equalsIgnoreCase("RETRO") || activeRow.getTransactionType().equalsIgnoreCase("FOREX")||
							activeRow.getTransactionType().equalsIgnoreCase("INVADJ")) {
						if(activeRow.getTransactionType().equalsIgnoreCase("INVADJ")){
							if(activeRow.getQuantity()!=null)
								activeRow.setClosingQuantity(activeQuantity+activeRow.getQuantity());
							else
								activeRow.setClosingQuantity(activeQuantity);
						}
						else
							activeRow.setClosingQuantity(activeQuantity);
						
						if(activeRow.getValue()!=null)
							activeRow.setClosingValue(activeValue+=activeRow.getValue());
						else
							activeRow.setClosingValue(activeValue);//to be changed
						
						activeRow.setClosingRate((float) (activeValue / activeQuantity));
						
						//change as to pick new rate after adjustments
						consumptionRate = activeRow.getClosingRate();
						activeRow.setQuantity(null);
						activeRow.setRate(null);
	//					wacReportList.add(activeRow);
					}
					
					if(activeRow.getTransactionDate().after(reportForm.getStartDate()))
						finalReportList.add(activeRow);
					if(finalReportList.size() == 1 && null != previousRow ){
						finalReportList.add(finalReportList.get(0));
						previousRow.setTransactionType("OPENING");
						previousRow.setQuantity(null);
						previousRow.setRate(null);
						previousRow.setValue(null);
						previousRow.setTransactionDate(reportForm.getStartDate());
						finalReportList.set(0, previousRow);
					}
					previousRow = activeRow;
					
				}
			
				//add date in thread map
				generateExcelThread.partId_data_map.put(object[0].toString() + "####" + object[1].toString(), finalReportList);
				System.out.println("size of map: " + generateExcelThread.getPartId_data_map().size());
			
				logger.debug("\n");
				
			}
			resultOfSucess = true;
			while(generateExcelThread.getPartId_data_map().size() > 0){
				Thread.sleep(2000l);
				System.out.println("Entries left: " + generateExcelThread.getPartId_data_map().size());
			}
			generateExcelThread.setRunning(false);
			generateExcelThread.join();
			System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%  STILL WRITING  %%%%%%%%%%%%%%%%%%%%%%%%%%");
			
		}
		catch (Throwable e) {
			e.printStackTrace();
		}
		finally{
			if(null != generateExcelThread){
				generateExcelThread.setRunning(false);
			}
			System.out.println("time taken: " + (System.currentTimeMillis() - startTime));
		}
//		logger.debug("resultOfSucess: "+resultOfSucess);
		return resultOfSucess;
	
	}
}
